#pragma once
#include <limits.h>
#include <iostream>
#include <iomanip>
template <class key, class value>
class HashTable
{
public:
	struct Node{
		value v;
		key k;
		Node* next;
		bool isSet;

		Node(key k, value v){
			isSet = true;
			this->k = k;
			this->v = v;
			next = nullptr;
		}
		Node(Node& n){
			isSet = true;
			this->k = n->k;
			this->v = n->v;
			next = nullptr;
		}
		Node(){
			isSet = false;
			next = nullptr;
		}
		
		void set(key k, value v){
			isSet = true;
			this->k = k;
			this->v = v;
			next = nullptr;
		}

		void append(key k, value v){
			if (next != nullptr){
				next->append(k, v);
			}
			else{
				next = new Node(k, v);
			}
		}

		bool operator==(Node& first){
			if (this->k == first->k){
				return true;
			}
			return false;
		}

		bool operator!=(Node& first){
			return !(this == first);
		}

		~Node(){
			delete next;
			delete k;
			delete v;
		};
	};

	struct Iterator{
		Iterator(Node* b, int upper){
			this->currentItem = b;
			this->upperBound = upper;
			currentArrayPos = 0;
			firstElem = b;
		}

		void operator++(){
			if (currentItem->next != nullptr){
				currentItem = currentItem->next;
			}
			else if (currentArrayPos < upperBound){
				do{
					currentArrayPos++;
					currentItem = firstElem + currentArrayPos;
				}
				while (currentItem->key == -1 && currentArrayPos < upperBound);
			}
			else{
				this->currentItem == nullptr;
			}
		};
		void operator++(int){
			if (currentItem->next != nullptr){
				currentItem = currentItem->next;
			}
			else if (currentArrayPos < upperBound){
				do{
					currentArrayPos++;
					currentItem = firstElem + currentArrayPos * (sizeof(Node*)+1);
				}
				while (!currentItem->isSet  && currentArrayPos < upperBound);
				if (currentArrayPos >= upperBound){
					currentItem = nullptr;
				}
			}
			else{
				this->currentItem = nullptr;
			}
		};

		Node* operator*(){
				return currentItem;
		}
		Node* currentItem;

		bool operator==(Iterator& first){
			if ((*this).currentItem == first.currentItem){
				return true;
			}
			return false;
		}

		bool operator!=(Iterator& first){
			return !((*this) == first);
		}
	private:
		int upperBound;
		int currentArrayPos;
		Node * firstElem;
	};


	~HashTable(void)
{
	delete [] hashTable;
}

HashTable(int buckets){
	hashMax = buckets;
	hashTable = new Node*[hashMax];
	for (int i = 0; i < buckets; i++){
		hashTable[i] = new Node();
	}
}


void add(key k, value v){
	int hashValue = hash(k);
	Node * bucketHead = hashTable[hashValue];
	if (bucketHead->isSet == false){
		bucketHead->set( k, v); 
	}
	else{
		bucketHead->append(k, v);
	}
}

Node* search(key k){
	int hashValue = hash(k);
	Node * item = hashTable[hashValue];
	while (item->k != k){
		if (item->next != nullptr){
			item = item->next;
		}
		else{
			return nullptr;
		}
	}
	return item;

}

Iterator begin(){
	for (int i = 0; i < hashMax; i++){
		if (hashTable[i] != nullptr){
			return Iterator(hashTable[i], hashMax);
		}
	}
	return Iterator(nullptr,hashMax);
}

Iterator end(){
	return Iterator(nullptr,hashMax);
}

void print(){
	std::cout << "Alla pekare: " << std::endl;
	for (auto it = begin(); it != end(); it++){
		std::cout << it.currentItem << std::endl;
	}
	std::cout << std::endl << std::endl;

	
	std::cout << "Alla data: " << std::endl;
	for (auto it = begin(); it != end(); it++){
		std::cout << std::setw(10) << hash((*it)->k);
		std::cout << std::setw(10) << (*it)->k;
		std::cout << std::setw(10) << (*it)->v;
		std::cout << std::setw(10) << (*it)->next;
		std::cout << std::endl;
	}
	std::cout << std::endl << std::endl;
	std::cin.get();
}

Node* operator[](int i){
		return hashTable[i];
	}


private:
	Node ** hashTable;
	int hashMax;
	int hash(key k){
		return (k ^ hashMax) % hashMax;
	}
};

